import { exportDefault, titleCase, deepClone } from '@/utils/index'
import ruleTrigger from './ruleTrigger'
const units = {
	KB: '1024',
	MB: '1024 / 1024',
	GB: '1024 / 1024 / 1024',
}
let confGlobal
const inheritAttrs = {
	vue: '',
	html: '',
}
// console.log(5555);
let buildOptionMethodArr = [] //记录option重复地址
let buildPropsMethodArr = [] //记录tableSelet重复地址

/**
 * 组装js 【入口函数】
 * @param {Object} formConfig 整个表单配置
 * @param {String} type 生成类型，文件或弹窗等
 */
export function makeUpJs(formConfig, type) {
	// console.log(formConfig)
	confGlobal = formConfig = deepClone(formConfig)
	const { formUrl, formSubmitType, formModel } = confGlobal
	const dataList = []
	const ruleList = []
	const optionsList = []
	const propsList = []
	const methodList = mixinMethod(type, formUrl, formSubmitType, formModel)
	const uploadVarList = []
	const created = []

	formConfig.fields.forEach(el => {
		buildAttributes(
			el,
			dataList,
			ruleList,
			optionsList,
			methodList,
			propsList,
			uploadVarList,
			created,
		)
	})

	const script = buildexport(
		formConfig,
		type,
		dataList.join('\n'),
		ruleList.join('\n'),
		optionsList.join('\n'),
		uploadVarList.join('\n'),
		propsList.join('\n'),
		methodList.join('\n'),
		created.join('\n'),
	)
	confGlobal = null
	return script
}

// 构建组件属性
function buildAttributes(
	scheme,
	dataList,
	ruleList,
	optionsList,
	methodList,
	propsList,
	uploadVarList,
	created,
) {
	const config = scheme.__config__
	const slot = scheme.__slot__
	buildData(scheme, dataList)
	buildRules(scheme, ruleList)

	// 特殊处理options属性
	if (scheme.options || (slot && slot.options)) {
		buildOptions(scheme, optionsList)
		if (config.dataType === 'dynamic') {
			const model = `${scheme.__vModel__}Options`
			const options = titleCase(model)
			const methodName = `get${options}`
			buildOptionMethod(methodName, model, methodList, scheme, created)
			// callInCreated(methodName, created)
		}
	}
	if (config.tag === 'table-select') {
		if (slot.tableDataUrl) {
			const model = `${scheme.__vModel__}Props`
			const options = titleCase(model)
			const methodName = `get${options}TableDataUrl`
			buildPropsMethod(
				methodName,
				`${model}.data`,
				methodList,
				scheme,
				slot.tableDataUrl,
				created,
				'data',
			)
			// callInCreated(methodName, created)
		}
		if (slot.tableHeaderUrl) {
			const model = `${scheme.__vModel__}Props`
			const options = titleCase(model)
			const methodName = `get${options}TableHeaderUrl`
			buildPropsMethod(
				methodName,
				`${model}`,
				methodList,
				scheme,
				slot.tableHeaderUrl,
				created,
				'header',
			)
			// callInCreated(methodName, created)
		}
	}
	// 处理props
	if (scheme.props && scheme.props.props) {
		buildProps(scheme, propsList)
	}

	// 处理el-upload的action
	if (scheme.action && config.tag === 'el-upload') {
		uploadVarList.push(
			`${scheme.__vModel__}Action: '${scheme.action}',
      ${scheme.__vModel__}fileList: [],`,
		)
		methodList.push(buildBeforeUpload(scheme))
		// 非自动上传时，生成手动上传的函数
		if (!scheme['auto-upload']) {
			methodList.push(buildSubmitUpload(scheme))
		}
	}

	// 构建子级组件属性
	if (config.children) {
		config.children.forEach(item => {
			buildAttributes(
				item,
				dataList,
				ruleList,
				optionsList,
				methodList,
				propsList,
				uploadVarList,
				created,
			)
		})
	}
}

// 在Created调用函数
function callInCreated(methodName, created) {
	created.push(`this.${methodName}()`)
}

// 混入处理函数
function mixinMethod(type, formUrl, formSubmitType, formModel) {
	const list = []
	const minxins = confGlobal.formBtns
		? {
				submitForm: `submitForm() {
        this.$refs['${confGlobal.formRef}'].validate(valid => {
          if(valid){
            axios({
              method: '${formSubmitType}',
              url: '${formUrl}',
              data: this['${formModel}']
            }).then(({
              data
            }) => {
							if(data.code === 'success') {
								this.$message.success(data.msg)
								if(saveSuccessCallBack){
									saveSuccessCallBack()
								}
							}else {
								this.$message.error(data.msg)
							}
            }).catch(e => {
                console.log(e);
              })
          }else {
            this.$message.error("请检查表单内容")
          }
          // TODO 提交表单
        })
      },`,
				resetForm: `resetForm() {
        this.$refs['${confGlobal.formRef}'].resetFields()
      },`,
		  }
		: null

	const methods = minxins
	if (methods) {
		Object.keys(methods).forEach(key => {
			list.push(methods[key])
		})
	}

	return list
}

// 构建data
function buildData(scheme, dataList) {
	const config = scheme.__config__
	if (scheme.__vModel__ === undefined) return
	const defaultValue = JSON.stringify(config.defaultValue)
	dataList.push(`${scheme.__vModel__}: ${defaultValue},`)
}

// 构建校验规则
function buildRules(scheme, ruleList) {
	// debugger
	const config = scheme.__config__
	if (scheme.__vModel__ === undefined) return
	const rules = []
	if (ruleTrigger[config.tag]) {
		if (config.required) {
			const type = Array.isArray(config.defaultValue) ? "type: 'array'," : ''
			let message = Array.isArray(config.defaultValue)
				? `请至少选择一个${config.label}`
				: scheme.placeholder
			if (message === undefined) message = `${config.label}不能为空`
			rules.push(
				`{ required: true, ${type} message: '${message}', trigger: '${
					ruleTrigger[config.tag]
				}' }`,
			)
		}
		if (config.regList && Array.isArray(config.regList)) {
			config.regList.forEach(item => {
				if (item.pattern) {
					rules.push(
						// eslint-disable-next-line no-eval
						`{ pattern: ${eval(item.pattern)}, message: '${
							item.message
						}', trigger: '${ruleTrigger[config.tag]}' }`,
					)
				}
			})
		}
		ruleList.push(`${scheme.__vModel__}: [${rules.join(',')}],`)
	}
}

// 构建options
function buildOptions(scheme, optionsList) {
	if (scheme.__vModel__ === undefined) return
	// el-cascader直接有options属性，其他组件都是定义在slot中，所以有两处判断
	let { options } = scheme
	if (!options) options = scheme.__slot__.options
	if (scheme.__config__.dataType === 'dynamic') {
		options = []
	}
	const str = `${scheme.__vModel__}Options: ${JSON.stringify(options)},`
	optionsList.push(str)
}

function buildProps(scheme, propsList) {
	// console.log(scheme, 3333)
	let str = ''
	if (scheme.__config__.tag === 'table-select') {
		let obj = {}
		if (scheme.__slot__.tableDataUrl) {
			obj.data = []
		} else {
			obj.data = scheme.props.data
		}
		if (scheme.__slot__.tableHeaderUrl) {
			obj.props = {}
			obj.filterFields = []
			obj.columns = []
		} else {
			obj.props = scheme.props.props
			obj.filterFields = scheme.props.filterFields
			obj.columns = scheme.props.columns
		}
		str = `${scheme.__vModel__}Props: ${JSON.stringify(obj)},`
	} else {
		str = `${scheme.__vModel__}Props: ${JSON.stringify(scheme.props.props)},`
	}
	propsList.push(str)
}

// el-upload的BeforeUpload
function buildBeforeUpload(scheme) {
	const config = scheme.__config__
	const unitNum = units[config.sizeUnit]
	let rightSizeCode = ''
	let acceptCode = ''
	const returnList = []
	if (config.fileSize) {
		rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${config.fileSize}
    if(!isRightSize){
      this.$message.error('文件大小超过 ${config.fileSize}${config.sizeUnit}')
    }`
		returnList.push('isRightSize')
	}
	if (scheme.accept) {
		acceptCode = `let isAccept = new RegExp('${scheme.accept}').test(file.type)
    if(!isAccept){
      this.$message.error('应该选择${scheme.accept}类型的文件')
    }`
		returnList.push('isAccept')
	}
	const str = `${scheme.__vModel__}BeforeUpload(file) {
    ${rightSizeCode}
    ${acceptCode}
    return ${returnList.join('&&')}
  },`
	return returnList.length ? str : ''
}

// el-upload的submit
function buildSubmitUpload(scheme) {
	const str = `submitUpload() {
    this.$refs['${scheme.__vModel__}'].submit()
  },`
	return str
}

function buildOptionMethod(methodName, model, methodList, scheme, created) {
	const config = scheme.__config__
	//i => {url: url, value: [...value]}
	let rpt = buildOptionMethodArr.find(i => i.url == config.url)
	let str = ''
	if (rpt && rpt.value && Array.isArray(rpt.value)) {
		rpt.value.push(
			`this.${model} = data.${config.dataPath} ? data.${config.dataPath} : data`,
		)
		str = `${methodName}() {
			axios({
				method: '${config.method}',
				url: '${config.url}'
			}).then(resp => {
				var { data } = resp
			 ${rpt.value.join(';')}
			})
		},`
		let oldStrIndex = methodList.findIndex(i => i.includes(config.url))
		methodList.splice(oldStrIndex, 1)
		let oldCreadIndex = created.findIndex(i => i.includes(methodName))
		created.splice(oldCreadIndex, 1)
	} else {
		str = `${methodName}() {
			axios({
				method: '${config.method}',
				url: '${config.url}'
			}).then(resp => {
				var { data } = resp;
				this.${model} = data.${config.dataPath} ?  data.${config.dataPath} : data;
			})
		},`
		buildOptionMethodArr.push({
			url: config.url,
			value: [
				`this.${model} = data.${config.dataPath} ? data.${config.dataPath} : data`,
			],
		})
	}

	methodList.push(str)
	callInCreated(methodName, created)
}
function buildPropsMethod(
	methodName,
	model,
	methodList,
	scheme,
	url,
	created,
	type,
) {
	// console.log(methodName, url, buildPropsMethodArr);
	const config = scheme.__config__
	let rpt = buildPropsMethodArr.find(i => i.url == url)
	let str = ''
	if (rpt && rpt.value && Array.isArray(rpt.value)) {
		// rpt.value.push(`this.${model} = data`)
		if (type == 'data') {
			rpt.value.push(`this.${model} = data.data`)
		} else if (type == 'header') {
			rpt.value.push(`this.${model}.columns = data.data.columns;
			this.${model}.filterFields = data.data.filterFields;
			this.${model}.props = data.data.props;`)
		}
		str = `${methodName}() {
			axios({
				method: '${config.method || 'get'}',
				url: '${url}'
			}).then(resp => {
				var { data } = resp
			 ${rpt.value.join(';')}
			})
		},`
		let oldStrIndex = methodList.findIndex(i => i.includes(url))
		methodList.splice(oldStrIndex, 1)
		let oldCreadIndex = created.findIndex(i => i.includes(methodName))
		created.splice(oldCreadIndex, 1)
	} else {
		if (type == 'data') {
			str = `${methodName}() {
				axios({
					method: '${config.method || 'get'}',
					url: '${url}'
				}).then(resp => {
					var { data } = resp
					this.${model} = data.data
				})
			},`
			// console.log(str);
			buildPropsMethodArr.push({
				url: url,
				value: [`this.${model} = data.data`],
			})
		} else if (type == 'header') {
			str = `${methodName}() {
				axios({
					method: '${config.method || 'get'}',
					url: '${url}'
				}).then(resp => {
					var { data } = resp
					this.${model}.columns = data.data.columns
					this.${model}.filterFields = data.data.filterFields
					this.${model}.props = data.data.props
				})
			},`
			buildPropsMethodArr.push({
				url: url,
				value: [
					`this.${model}.columns = data.data.columns;
			this.${model}.filterFields = data.data.filterFields;
			this.${model}.props = data.data.props;`,
				],
			})
		}
	}
	methodList.push(str)
	// console.log(methodList);
	callInCreated(methodName, created)
}

// js整体拼接
function buildexport(
	conf,
	type,
	data,
	rules,
	selectOptions,
	uploadVar,
	props,
	methods,
	created,
) {
	let str = ''
	if (type === 'html') {
		str = `{
      ${inheritAttrs[type]}
      el: '#app',
      components: {},
      props: [],
      data : {
          ${conf.formModel}: {
            ${data}
          },
          ${conf.formRules}: {
            ${rules}
          },
          ${uploadVar}
          ${selectOptions}
          ${props}
      },
      computed: {},
      watch: {},
      created () {
        ${created}
      },
      mounted () {
				window.echoData = (data) => {
					Object.keys(data).forEach(e => {
						this.$set(this.${conf.formModel}, e, data[e])
					});
				}
			},
      methods: {
        ${methods}
      }
    }`
	} else if (type === 'vue') {
		str = `${exportDefault}{
      ${inheritAttrs[type]}
      components: {},
      props: [],
      data () {
        return {
          ${conf.formModel}: {
            ${data}
          },
          ${conf.formRules}: {
            ${rules}
          },
          ${uploadVar}
          ${selectOptions}
          ${props}
        }
      },
      computed: {},
      watch: {},
      created () {
        ${created}
      },
      mounted () {},
      methods: {
        ${methods}
      }
    }`
	}

	return str
}
